using System;
using System.Text;
using System.Data;
using System.Collections;
using System.Collections.Generic;

using Framework;
using System.IO;
using System.Drawing;

namespace SchemaMonitor
{
    //Table-Mapping Class (Customisable half)
    public partial class CPhoto
    {
        #region Constants
        #endregion

        #region Constructors (Public)
        //Default Connection String
        public CPhoto() : base() {}

        //Alternative Connection String
        public CPhoto(CDataSrc dataSrc) : base(dataSrc) {}
        
        //Hidden  (UI code should use cache instead)
        protected internal CPhoto(long iD) : base(iD) {}
        protected internal CPhoto(CDataSrc dataSrc, long iD) : base(dataSrc, iD) {}
        protected internal CPhoto(CDataSrc dataSrc, long iD, IDbTransaction txOrNull) : base(dataSrc, iD, txOrNull) { }
        #endregion

        #region Default Values
        protected override void InitValues_Custom()
        {
            //_sampleDateCreated = DateTime.Now;
            //_sampleSortOrder   = 0;
        }
        #endregion
        
        #region Default Connection String
        protected override CDataSrc DefaultDataSrc()  { return CConfig.Photos;  }
		#endregion

		#region Properties - Relationships
		//Relationships - Foriegn Keys (e.g parent)

		public CPhotoHash PhotoHash { get { return CPhotoHash.Cache.GetById(this.Md5); } }

		//Relationships - Collections (e.g. children)
		#endregion

		#region Properties - Customisation
		//Derived/ReadOnly (e.g. xml classes, presentation logic)
		#endregion

		#region Save/Delete Overrides
		//Can Override base.Save/Delete (e.g. Cascade deletes, or insert related records)
		#endregion

		#region Custom Database Queries
		public static CPhoto GetPhoto(long id)
		{
			return CPhoto.Cache.GetById(id);
			/*
			var pp = new CPhoto().SelectById(id);
			if (pp.Count == 0)
				return null;
			return pp[0];
			*/
		}
		static readonly string SQL = string.Concat("SELECT ", PRIMARY_KEY_NAME, " FROM ", TABLE_NAME, " WHERE UserId=@userId");
		public static List<long> GetIds(CUser u) { return GetIds(u.UserId); }
		public static List<long> GetIds(long userId)
		{
			return CPhoto.Cache.GetByUserId(userId).Ids;
			/*
			var d = new CPhoto().DataSrc;
			var ids = d.MakeListString(new CCommand(SQL, new CNameValueList("userId", userId), false));
			var list = new List<long>(ids.Count);
			foreach (var i in ids)
				list.Add(long.Parse(i));
			return list;
			*/
		}
		public static CPhotoList PutPhotos(long userId, string folderPath = @"D:\facebook-jeremyconnell7\photos\")
		{
			var ids = GetIds(userId);
			return PutPhotos(userId, folderPath, ids);
		}
		private static CPhotoList PutPhotos(long userId, string folderPath, List<long> ids)
		{
			return PutPhotos(userId, folderPath, ids, new CPhotoList());
		}
		private static CPhotoList PutPhotos(long userId, string folderPath, List<long> ids, CPhotoList list)
		{
			foreach (var i in Directory.GetFiles(folderPath))
			{
				var p = PutPhoto(userId, i, ids, ids);
				if (null == p)
					continue;
				p.PhotoHash.PhotoBlob = null;
				list.Add(p);
			}

			foreach (var i in Directory.GetDirectories(folderPath))
				PutPhotos(userId, i, ids, list);
			return list;
		}
		public static CPhoto PutPhoto(long userId, string filePath, List<long> storedIds, List<long> allIds)
		{
			string fileName = Path.GetFileName(filePath);
			if (!filePath.EndsWith(".jpg"))
			{
				Console.WriteLine("Bad extension: " + fileName);
				return null;
			}

			var prefix = Path.GetFileNameWithoutExtension(filePath);
			long id = 0;
			if (!long.TryParse(prefix, out id))
			{
				Console.WriteLine("Bad name: " + fileName);
				return null;
			}

			if (storedIds.Contains(id))
				return null;

			var bin = File.ReadAllBytes(filePath);
			var created = File.GetCreationTime(filePath);

			try
			{
				if (allIds.Contains(id))
					return null;
				try
				{
					var s = Image.FromFile(filePath).Size;
					return PutPhoto(id, userId, bin, created, s.Width, s.Height);
				}
				catch
				{
					Console.WriteLine("bad img: " + fileName);
					return PutPhoto(id, userId, bin, created);
				}
			}
			finally
			{
				if (!storedIds.Contains(id))
					storedIds.Add(id);
				Console.WriteLine(CUtilities.FileSize(bin.Length) + " " + fileName + " " + storedIds.Count);
			}
		}
		public static CPhoto PutPhoto(long id, long userId, byte[] blob, DateTime created, int width = int.MinValue, int height = int.MinValue)
		{
			var md5 = CBinary.MD5_(blob);
			var ph = CPhotoHash.Cache.GetById(md5);
			if (null == ph)
			{
				ph = new CPhotoHash();
				ph.PhotoBlob = blob;
				ph.PhotoCreated = created;
				ph.PhotoHeight = height;
				ph.PhotoWidth = width;
				ph.PhotoMd5 = md5;
				ph.PhotoSize = blob.Length;
				ph.Save();
				ph.PhotoBlob = null;
			}
			var p = new CPhoto();
			p.ID = id;
			p.UserId = userId;
			p.Md5 = md5;
			p.Created = created;
			try
			{
				p.Save();
			}
			catch (Exception ex)
			{
				var pp = GetPhoto(id);
				if (null != pp)
					return pp;
				throw ex;
			}
			return p;
		}
		//For Stored Procs can use: MakeList (matching schema), or DataSrc.ExecuteDataset (reports etc)
		//For Dynamic sql, can use: SelectSum, SelectDistinct, SelectCount, SelectWhere (inherited methods)
		//                see also: SelectBy[FK], Search and Count (auto-generated sample queries)
		#endregion


		#region Searching (Optional)
		//For cached classes, custom seach logic resides in static methods on the list class
		// e.g. CPhoto.Cache.Search("...")

		//See also the auto-generated methods based on indexes
		//' e.g. CPhoto.Cache.GetBy...
		#endregion

		#region Caching Details
		//Cache Key
		internal static string CACHE_KEY = typeof(CPhoto).ToString();    //TABLE_NAME

        //Cache data
        private static CPhotoList LoadCache()  {   return new CPhoto().SelectAll();   }
        //Cache Timeout
        private static void SetCache(CPhotoList value)
        {
            if (null != value)  
                value.Sort(); 
            CCache.Set(CACHE_KEY, value);    //Optional parameter can override timeout (otherwise uses config-settings, which default to 3hrs)
        }
        //Helper Method
        private CPhoto CacheGetById(CPhotoList list)  { return list.GetById(this.ID);    }
        #endregion

        #region Cloning
        public CPhoto Clone(CDataSrc target, IDbTransaction txOrNull) //, int parentId)
        {
            //Shallow copy: Copies the immediate record, excluding autogenerated Pks
            CPhoto copy = new CPhoto(this, target);

            //Deep Copy - Child Entities: Cloned children must reference their cloned parent
            //copy.SampleParentId = parentId;

            copy.Save(txOrNull);

            //Deep Copy - Parent Entities: Cloned parents also clone their child collections
            //this.Children.Clone(target, txOrNull, copy.ID);

            return copy;
        }
        #endregion

        #region ToXml
        protected override void ToXml_Custom(System.Xml.XmlWriter w)
        {
            //Store(w, "Example", this.Example)
        }
        #endregion
    }
}